package thread.wang_bao_ling.c24;

import org.junit.Test;

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;

/**
 * @Auther: luobiao
 * @Date: 2020/7/20 22:48
 * @Description:
 */
public class CompletionStageTest {

    /**
     * 描述串行关系
     */
    @Test
    public void test1() {
        CompletableFuture<String> f0 =
                CompletableFuture.supplyAsync(
                        () -> {
                            sleep(2, TimeUnit.SECONDS);
                            return "hello";
                        })
                        .thenApplyAsync(s -> {
                            sleep(2, TimeUnit.SECONDS);
                            return s + " qq";
                        })
                        .thenApplyAsync(s -> {
                            sleep(2, TimeUnit.SECONDS);
                            return s.toUpperCase();
                        });

        System.out.println(f0.join());
    }

    /**
     * 描述AND汇聚关系
     */
    @Test
    public void test2() {
        CompletableFuture<Integer> f1 =
                CompletableFuture.supplyAsync(() -> {
                    int t = getRandomTime("f1");
                    sleep(t, TimeUnit.SECONDS);
                    return t;
                });

        CompletableFuture<Integer> f2 =
                CompletableFuture.supplyAsync(() -> {
                    int t = getRandomTime("f2");
                    sleep(t, TimeUnit.SECONDS);
                    return t;
                });

        //f1和f2是and汇聚，要等两个任务都并行执行完成，所以最终执行时间为耗时最长的那个任务时间
        //注意thenCombine第二个参数为BiFunction，因为会等两个任务都并行执行完毕，所以能取到两个任务的执行结果
        CompletableFuture<Integer> f3 =
                f1.thenCombine(f2, (t1, t2) -> {
                    return t1 > t2 ? t1 : t2;
                });
        System.out.println("执行时间：" + f3.join());
    }

    /**
     * 描述AND汇聚关系2
     */
    @Test
    public void test22() {
        System.out.println(System.currentTimeMillis()/1000);
        CompletableFuture.runAsync(() -> {
            sleep(5, TimeUnit.SECONDS);
        }).thenCombine(
                CompletableFuture.runAsync(() -> {
                    sleep(3, TimeUnit.SECONDS);
                }), (t1, t2) -> null
        ).thenCombine(
                CompletableFuture.runAsync(() -> {
                    sleep(5, TimeUnit.SECONDS);
                }), (t1, t2) -> null
        ).thenCombine(
                CompletableFuture.runAsync(() -> {
                    sleep(3, TimeUnit.SECONDS);
                }), (t1, t2) -> null
        ).join();
        System.out.println(System.currentTimeMillis()/1000);
    }

    /**
     * 描述OR汇聚关系
     */
    @Test
    public void test3() {
        CompletableFuture<Integer> f1 =
                CompletableFuture.supplyAsync(() -> {
                    int t = getRandomTime("f1");
                    sleep(t, TimeUnit.SECONDS);
                    return t;
                });

        CompletableFuture<Integer> f2 =
                CompletableFuture.supplyAsync(() -> {
                    int t = getRandomTime("f2");
                    sleep(t, TimeUnit.SECONDS);
                    return t;
                });

        //f1和f2是or汇聚，谁先执行就使用谁的结果，所以最终执行时间为耗时最短的那个任务时间
        //注意applyToEither第二个参数为Function，因为只会使用一个任务的返回结果
        CompletableFuture<Integer> f3 =
                f1.applyToEither(f2, s -> s);
        System.out.println("执行时间：" + f3.join());
    }

    @Test
    public void test4() {
        CompletableFuture<Integer> f0 = CompletableFuture
                .supplyAsync(() -> (7 / 0))
                .thenApply(r -> r * 10)
                .exceptionally(e -> 0);
        System.out.println(f0.join());
    }



    /**
     * 随机生成休眠的秒数（1-6）
     *
     * @param name
     * @return
     */
    public int getRandomTime(String name) {
        Random random = new Random();
        int t = random.nextInt(5) + 1;
        System.out.println("t=" + t);
        return t;
    }

    void sleep(int t, TimeUnit u) {
        try {
            u.sleep(t);

        } catch (InterruptedException e) {
        }
    }
}
